﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using The_Square_Saga.Utils;
using The_Square_Saga.Objects.Interface;

namespace The_Square_Saga.Objects
{
    abstract class AnimatedGameEntity : MovableGameEntity, IGameEntity
    {
        #region animation
            /// <summary>
            /// The animations for this sprite
            /// </summary>
            private List<Animation> animations;

            ///<summary>
            /// The current animation which will be played
            /// </summary>
            private Animation currentAnimation;

            /// <summary>
            /// the current frame
            /// </summary>
            protected Point currentFrame;

            /// <summary>
            /// the previous frame
            /// </summary>
            protected Point prevFrame;

            /// <summary>
            /// Time of last frame
            /// </summary>
            private float lastFrametick;

            /// <summary>
            /// Current game time
            /// </summary>
            private float currentTime;

            /// <summary>
            /// The source rectangle
            /// </summary>
            public Rectangle sourceRectangle;
        #endregion

        #region Properties

        public Point CurrentFrame
        {
            get { return currentFrame; }
        }

        public Rectangle AnimationRect(Animation animation)
        {
            if (currentAnimation != null)
            {
                return new Rectangle(animation.getFrameX() * (int)animation.getFrameSize().X,
                                     animation.getFrameY() * (int)animation.getFrameSize().Y,
                                     (int)animation.getFrameSize().X,
                                     (int)animation.getFrameSize().Y);
            }
            else
                return new Rectangle(0, 0, this.getImage().Width, this.getImage().Height);
        }

        /// <summary>
        /// Return the current animation
        /// </summary>
        public Animation CurrentAnimation
        {
            get { return currentAnimation; }
        }

        //defines if this object could be moved
        private bool isMobile;

        #endregion

        /// <summary>
        /// the effects for this sprites
        /// </summary>
        public SpriteEffects spriteEffects;

        /// <summary>
        /// Zorder of animation
        /// </summary>
        public float zOrder = 0;

        /// <summary>
        /// escala para redimensionar objetos
        /// </summary>
        public float scale = 1.0f;

        /// <summary>
        ///para rotacionar objetos
        /// </summary>
        public float rotation = 0.0f;

        public AnimatedGameEntity(Texture2D image, Vector2 position, bool isMobile=true)
            : base(image, position)
        {
            this.animations = new List<Animation>();
            
            this.spriteEffects = SpriteEffects.None;
            this.lastFrametick = 0;
            this.currentFrame.X = 1;
            this.currentFrame.Y = 1;
            sourceRectangle = new Rectangle(0, 0, CollisionRect.Width, CollisionRect.Height);
            
        }

        #region Animate

        /// <summary>
        /// Adds an animation
        /// </summary>
        /// <param name="animation">An animation</param>
        public void addAnimation(Animation animation)
        {
            if (!Util.isNull(animation)) // do not add null animation
            {
                animations.Add(animation);
            }
        }

        /// <summary>
        /// Removes an animation
        /// </summary>
        /// <param name="animation">An animation</param>
        public void removeAnimation(Animation animation)
        {
            if (!Util.isNull(animation)) // do not add null animation
            {
                animations.Remove(animation);
            }
        }

        /// <summary>
        /// Load and set as currentAnimation
        /// </summary>
        /// <param name="name">An animation name</param>
        private void loadAnimation(string name)
        {
            if (currentAnimation != null)
            {
                if (currentAnimation.getName() == name)
                {
                    return;
                }
            }

            currentAnimation = searchAnimation(name);

            if (currentAnimation != null)
            {
                currentFrame.X = currentAnimation.getFrameX();
                currentFrame.Y = currentAnimation.getFrameY();

                prevFrame.X = currentAnimation.getFrameX();
                prevFrame.Y = currentAnimation.getFrameY();

                int srcFrameX = currentFrame.X * (int)currentAnimation.getFrameSize().X;
                int srcFrameY = currentFrame.Y * (int)currentAnimation.getFrameSize().Y;

                lastFrametick = 0;
                sourceRectangle = new Rectangle(srcFrameX, srcFrameY, (int)currentAnimation.getFrameSize().X, (int)currentAnimation.getFrameSize().Y);
            }
        }

        /// <summary>
        /// Search an animation
        /// </summary>
        /// <param name="name">An animation name</param>
        /// <returns>An animation</returns>
        public Animation searchAnimation(string name)
        {
            return animations.Find(delegate(Animation a)
            {
                return a.getName() == name;
            });
        }

        /// <summary>
        /// Check if an animation finished.
        /// </summary>
        /// <returns>True, if animation over.\nIf the animation name is not the name of the current animation or animation not finished, returns false</returns>
        public bool isAnimationFinished()
        {
            if (currentAnimation.getAxis() == Axis.X)
            {
                return ((currentFrame.X < prevFrame.X) && (currentTime + 18 >= lastFrametick + currentAnimation.getFrameTime()));
            }
            else
            {
                return ((currentFrame.Y < prevFrame.Y) && (currentTime + 18 >= lastFrametick + currentAnimation.getFrameTime()));
            }
        }

        /// <summary>
        /// Set the currentAnimation. That animation must be inside the list of animations
        /// </summary>
        /// <param name="name">A name of an animation</param>
        public void setCurrentAnimation(string name)
        {
            loadAnimation(name);
        }

        /// <summary>
        /// Plays an animation
        /// </summary>
        /// <param name="name">The animation name for play</param>
        public void playCurrentAnimation(string name, GameTime gameTime)
        {
            currentTime = (float)gameTime.TotalGameTime.TotalMilliseconds;

            if (currentTime >= lastFrametick + currentAnimation.getFrameTime())
            {
                lastFrametick = currentTime;

                sourceRectangle.X = (int)(currentFrame.X * currentAnimation.getFrameSize().X);
                sourceRectangle.Y = (int)(currentFrame.Y * currentAnimation.getFrameSize().Y);

                // if there is not more than 1 frame, do not make animation
                if (currentAnimation.getTotalFrames() <= 1)
                    return;

                if (currentAnimation.getAxis() == Axis.X)
                {
                    if (currentFrame.X != prevFrame.X)
                        prevFrame.X = currentFrame.X;

                    currentFrame.X++;
                    if (currentFrame.X >= currentAnimation.getTotalFrames())
                        currentFrame.X = currentAnimation.getFrameX();

                }
                else
                {
                    if (currentFrame.Y != prevFrame.Y)
                        prevFrame.Y = currentFrame.Y;

                    currentFrame.Y++;
                    if (currentFrame.Y >= currentAnimation.getTotalFrames())
                        currentFrame.Y = currentAnimation.getFrameY();
                }
            }
        }

        #endregion

        /// <summary>
        /// Clear the sprite effects on this object
        /// </summary>
        public void deFlipHorizontally()
        {
            this.spriteEffects = SpriteEffects.None;
        }

        /// <summary>
        /// Flips the image in horizontal. If the sprite is fliped, him returns to original state
        /// </summary>
        public void flipHorizontally()
        {
            this.spriteEffects = SpriteEffects.FlipHorizontally;            
        }

        /// <summary>
        /// Flips the image in vertical. If the sprite is fliped, him returns to original state
        /// </summary>
        public void flipVertically()
        {
            if (spriteEffects == SpriteEffects.None)
            {
                spriteEffects = SpriteEffects.FlipVertically;
            }
        }
        
        public override void update(GameTime gameTime)
        {
            if (isMobile)
            {
                this.ApplyMovement();
            }

            if (!Util.isNull(currentAnimation))
            {
                playCurrentAnimation(currentAnimation.getName(), gameTime);
            
            }
            else
            {
                //throw new Exception("the animation is null");
            }
        }

        public override void draw(SpriteBatch spriteBatch)
        {
                spriteBatch.Draw(this.getImage(), this.position , sourceRectangle, Color.White, rotation, Vector2.Zero, scale, spriteEffects, zOrder);
        }
    }
}
